@kernel.chat/kbot 3.64.0 → 3.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <p align="center">
4
4
  <strong>kbot</strong><br>
5
- Open-source terminal AI agent. 35 agents. 670+ tools. 20 providers. Science, finance, security, and more.
5
+ Open-source terminal AI agent. 35 agents. 686+ tools. 20 providers. Science, finance, security, and more.
6
6
  </p>
7
7
 
8
8
  <p align="center">
@@ -30,17 +30,42 @@ Most terminal AI agents lock you into one provider, one model, one way of workin
30
30
  - **Runs fully offline** — Embedded llama.cpp, Ollama, LM Studio, or Jan. $0, fully private.
31
31
  - **Learns your patterns** — Bayesian skill ratings + pattern extraction. Gets faster over time.
32
32
  - **35 specialist agents** — auto-routes your request to the right expert (coder, researcher, writer, guardian, quant, and 30 more).
33
- - **670+ tools** — files, bash, git, GitHub, web search, deploy, database, game dev, VFX, research, science, finance, security, and more.
33
+ - **686+ tools** — files, bash, git, GitHub, web search, deploy, database, game dev, VFX, research, science, finance, security, and more.
34
34
  - **Programmatic SDK** — use kbot as a library in your own apps.
35
35
  - **MCP server built in** — plug kbot into Claude Code, Cursor, VS Code, Zed, or Neovim as a tool provider.
36
36
 
37
+ ## Highlights
38
+
39
+ ### Dream Engine — Your AI Remembers You
40
+
41
+ After each session, kbot "dreams" — consolidating what it learned about you into durable insights using local Ollama models. $0 cost. Insights feed back into future sessions automatically.
42
+
43
+ ```
44
+ kbot dream run # Trigger consolidation
45
+ kbot dream status # See what kbot learned
46
+ kbot dream journal # Full insight history
47
+ kbot dream search # Find specific memories
48
+ ```
49
+
50
+ 5-tier memory: pattern cache -> solution index -> user profile -> dream journal -> passive scanner. All tiers feed each other through the dream engine.
51
+
52
+ ### Audit Any Repo in One Command
53
+
54
+ ```
55
+ kbot audit facebook/react
56
+ kbot audit --share vercel/next.js # Creates a public Gist
57
+ kbot audit --badge your/repo # Badge for your README
58
+ ```
59
+
60
+ Checks security, documentation, code quality, CI/CD, community health, and DevOps. Scored out of 100, graded A-F. Add the badge to your README.
61
+
37
62
  ### How it compares
38
63
 
39
64
  | | kbot | Claude Code | Codex CLI | Aider | OpenCode |
40
65
  |---|---|---|---|---|---|
41
66
  | AI providers | 20 | 1 | 1 | 6 | 75+ |
42
67
  | Specialist agents | 35 | 0 | 0 | 0 | 0 |
43
- | Built-in tools | 600+ | ~20 | ~15 | ~10 | ~15 |
68
+ | Built-in tools | 686+ | ~20 | ~15 | ~10 | ~15 |
44
69
  | Science tools | 114 | 0 | 0 | 0 | 0 |
45
70
  | Learning engine | Yes | No | No | No | No |
46
71
  | Offline mode | Embedded + Ollama | No | No | Ollama | Ollama |
@@ -93,7 +118,7 @@ kbot auto-routes to the right agent for each task. Or pick one with `--agent <na
93
118
  | **Domain** | infrastructure, quant, investigator, oracle, chronist, sage, communicator, adapter |
94
119
  | **Presets** | claude-code, cursor, copilot, creative, developer |
95
120
 
96
- ## 600+ Tools
121
+ ## 686+ Tools
97
122
 
98
123
  | Category | Examples |
99
124
  |----------|---------|
@@ -185,7 +210,7 @@ graph TD
185
210
  D -->|Multi-step| F[Autonomous Planner]
186
211
  E --> G[Provider API + Tool Loop]
187
212
  F --> G
188
- G --> H{600+ Tools}
213
+ G --> H{686+ Tools}
189
214
  H --> I[File ops, bash, git, GitHub, search, deploy, DB, game dev...]
190
215
  G --> J[Learning Engine]
191
216
  J --> K[Patterns + Solutions + User Profile]
@@ -218,7 +243,11 @@ Works with Claude Code, Cursor, VS Code, Windsurf, Zed, Neovim.
218
243
  | `kbot auth` | Configure API key |
219
244
  | `kbot local` | Use local AI (Ollama, embedded, LM Studio, Jan) |
220
245
  | `kbot serve` | Start HTTP REST + SSE streaming server |
221
- | `kbot audit <repo>` | Security + quality audit of any GitHub repo |
246
+ | `kbot audit <repo>` | Security + quality audit of any GitHub repo (A-F grade) |
247
+ | `kbot dream run` | Consolidate session learnings into durable insights |
248
+ | `kbot dream status` | See what kbot has learned about you |
249
+ | `kbot dream journal` | Full insight history |
250
+ | `kbot dream search` | Find specific memories |
222
251
  | `kbot contribute <repo>` | Find good-first-issues and quick wins |
223
252
  | `kbot share` | Share conversation as GitHub Gist |
224
253
  | `kbot pair` | File watcher with auto-analysis |
package/dist/agent.js CHANGED
@@ -17,6 +17,7 @@ import { getMatrixSystemPrompt } from './matrix.js';
17
17
  import { buildFullLearningContext, findPattern, recordPattern, cacheSolution, updateProfile, classifyTask, extractKeywords, learnFromExchange, updateProjectMemory, shouldAutoTrain, selfTrain, } from './learning.js';
18
18
  import { getMemoryPrompt, addTurn, getPreviousMessages, getHistory } from './memory.js';
19
19
  import { getDreamPrompt, dreamAfterSession } from './dream.js';
20
+ import { setBuddyMood } from './buddy.js';
20
21
  import { notifyTurn, startMemoryScanner, stopMemoryScanner } from './memory-scanner.js';
21
22
  import { autoCompact, compressToolResult } from './context-manager.js';
22
23
  import { learnedRoute, recordRoute } from './learned-router.js';
@@ -1003,6 +1004,7 @@ Always quote file paths that contain spaces. Never reference internal system nam
1003
1004
  model: options.model || 'auto',
1004
1005
  message: originalMessage.slice(0, 200),
1005
1006
  });
1007
+ setBuddyMood('thinking');
1006
1008
  // Start passive memory scanner for this session
1007
1009
  startMemoryScanner();
1008
1010
  // ── Gödel limits: detect undecidable loops and hand off to human ──
@@ -1531,6 +1533,8 @@ Always quote file paths that contain spaces. Never reference internal system nam
1531
1533
  };
1532
1534
  results.push(result);
1533
1535
  ui.onToolCallEnd(call.name, result.result, result.error ? result.result : undefined, result.duration_ms);
1536
+ // Update buddy mood based on tool outcome
1537
+ setBuddyMood(result.error ? 'error' : 'success');
1534
1538
  // ── Observer: record tool call for cross-session learning ──
1535
1539
  try {
1536
1540
  const { recordObservation } = await import('./observer.js');
@@ -1598,6 +1602,7 @@ Always quote file paths that contain spaces. Never reference internal system nam
1598
1602
  }
1599
1603
  catch (err) {
1600
1604
  spinnerHandle?.stop();
1605
+ setBuddyMood('error');
1601
1606
  // ── Telemetry: session failure ──
1602
1607
  telemetry.emit('session_end', { status: 'failed', error: String(err), toolCallCount });
1603
1608
  telemetry.destroy().catch(() => { });
@@ -1616,7 +1621,10 @@ Always quote file paths that contain spaces. Never reference internal system nam
1616
1621
  // ── Memory Scanner: stop and persist session stats ──
1617
1622
  stopMemoryScanner();
1618
1623
  // ── Dream Engine: consolidate session memories (non-blocking, $0 via Ollama) ──
1624
+ setBuddyMood('learning');
1619
1625
  dreamAfterSession(sessionId);
1626
+ // Session complete — buddy returns to idle
1627
+ setBuddyMood('idle');
1620
1628
  const content = lastResponse?.content || 'Reached maximum tool iterations.';
1621
1629
  return {
1622
1630
  content,
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import { createInterface } from 'node:readline';
12
12
  import { existsSync } from 'node:fs';
13
13
  import { join, basename } from 'node:path';
14
+ import { homedir } from 'node:os';
14
15
  import { Command } from 'commander';
15
16
  import { loadConfig, setupByok, setupEmbedded, isByokEnabled, isLocalProvider, disableByok, detectProvider, getByokProvider, PROVIDERS, setupOllama, setupKbotLocal, isOllamaRunning, listOllamaModels, warmOllamaModelCache } from './auth.js';
16
17
  import { runAndPrint, runAgent, runAgentFromCheckpoint } from './agent.js';
@@ -26,6 +27,7 @@ import { banner, bannerCompact, bannerAuth, prompt as kbotPrompt, printError, pr
26
27
  import { checkForUpdate, selfUpdate } from './updater.js';
27
28
  import { runTutorial } from './tutorial.js';
28
29
  import { syncOnStartup, schedulePush, flushCloudSync, isCloudSyncEnabled, setCloudToken, getCloudToken } from './cloud-sync.js';
30
+ import { getBuddy, getBuddyGreeting, formatBuddyStatus } from './buddy.js';
29
31
  import chalk from 'chalk';
30
32
  import { createRequire } from 'node:module';
31
33
  const __require = createRequire(import.meta.url);
@@ -2439,7 +2441,7 @@ async function main() {
2439
2441
  .option('--json', 'Output raw JSON')
2440
2442
  .option('--badge', 'Print only the badge markdown (for adding to READMEs)')
2441
2443
  .action(async (repo, auditOpts) => {
2442
- const { auditRepo, formatAuditReport } = await import('./tools/audit.js');
2444
+ const { auditRepo, formatAuditReport, formatAuditTerminal } = await import('./tools/audit.js');
2443
2445
  printInfo(`Auditing ${repo}...`);
2444
2446
  try {
2445
2447
  const result = await auditRepo(repo);
@@ -2453,14 +2455,16 @@ async function main() {
2453
2455
  console.log(`[![kbot audit: ${result.grade}](https://img.shields.io/badge/kbot_audit-${result.grade}_(${pct}%25)-${badgeColor})](https://www.npmjs.com/package/@kernel.chat/kbot)`);
2454
2456
  return;
2455
2457
  }
2456
- const report = formatAuditReport(result);
2457
- console.log(report);
2458
- // Auto-share as gist
2458
+ // Terminal gets the styled version; --share gist gets markdown
2459
+ const terminalReport = formatAuditTerminal(result);
2460
+ console.log(terminalReport);
2461
+ // Auto-share as gist (uses markdown format for portability)
2459
2462
  if (auditOpts.share) {
2460
2463
  printInfo('Sharing audit report...');
2461
2464
  try {
2465
+ const markdownReport = formatAuditReport(result);
2462
2466
  const { createGist } = await import('./share.js');
2463
- const url = createGist(report, `kbot-audit-${repo.replace('/', '-')}.md`, `kbot Audit: ${repo} — Grade ${result.grade}`, true);
2467
+ const url = createGist(markdownReport, `kbot-audit-${repo.replace('/', '-')}.md`, `kbot Audit: ${repo} — Grade ${result.grade}`, true);
2464
2468
  if (url?.startsWith('http')) {
2465
2469
  printSuccess(`Shared! ${url}`);
2466
2470
  printInfo(`Badge for ${repo}'s README:`);
@@ -3167,20 +3171,28 @@ async function main() {
3167
3171
  .description('Run a dream cycle now (uses local Ollama)')
3168
3172
  .action(async () => {
3169
3173
  const { dream } = await import('./dream.js');
3170
- printInfo('Dreaming... consolidating session memories with local AI');
3174
+ console.log();
3175
+ console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Engine')} ${chalk.dim('consolidating memories...')}`);
3176
+ console.log();
3171
3177
  const result = await dream();
3172
3178
  if (result.success) {
3173
- printSuccess(`Dream cycle #${result.cycle} complete`);
3174
- console.log(` New insights: ${result.newInsights}`);
3175
- console.log(` Reinforced: ${result.reinforced}`);
3176
- console.log(` Archived: ${result.archived} aged-out`);
3177
- console.log(` Duration: ${result.duration}ms`);
3179
+ console.log(` ${chalk.hex('#4ADE80')('✓')} ${chalk.bold(`Cycle #${result.cycle} complete`)} ${chalk.dim(`${result.duration}ms`)}`);
3180
+ console.log();
3181
+ console.log(` ${chalk.hex('#4ADE80')('+')} ${chalk.bold(String(result.newInsights))} new insights`);
3182
+ console.log(` ${chalk.hex('#A78BFA')('↻')} ${chalk.bold(String(result.reinforced))} reinforced`);
3183
+ if (result.archived > 0) {
3184
+ console.log(` ${chalk.dim('↓')} ${chalk.dim(`${result.archived} archived (aged out)`)}`);
3185
+ }
3186
+ console.log();
3187
+ console.log(` ${chalk.dim('View results:')} ${chalk.hex('#A78BFA')('kbot dream status')}`);
3178
3188
  }
3179
3189
  else {
3180
- printWarn(result.error || 'Dream cycle failed');
3181
- if (result.archived > 0)
3182
- console.log(` (Still archived ${result.archived} aged-out insights)`);
3190
+ console.log(` ${chalk.hex('#FBBF24')('!')} ${result.error || 'Dream cycle failed'}`);
3191
+ if (result.archived > 0) {
3192
+ console.log(` ${chalk.dim('↓')} ${chalk.dim(`${result.archived} insights archived (aging only)`)}`);
3193
+ }
3183
3194
  }
3195
+ console.log();
3184
3196
  });
3185
3197
  dreamCmd
3186
3198
  .command('status')
@@ -3188,23 +3200,86 @@ async function main() {
3188
3200
  .action(async () => {
3189
3201
  const { getDreamStatus } = await import('./dream.js');
3190
3202
  const { state, insights, archiveCount } = getDreamStatus();
3191
- console.log(chalk.bold('\nDream Engine'));
3192
- console.log(chalk.dim('═══════════════════'));
3193
- console.log(`Cycles: ${state.cycles}`);
3194
- console.log(`Last: ${state.lastDream || 'never'}`);
3195
- console.log(`Active: ${state.activeInsights} insights`);
3196
- console.log(`Archived: ${state.totalArchived} (${archiveCount} files)`);
3203
+ // ── Helper functions ──
3204
+ const W = 56; // inner width
3205
+ const box = {
3206
+ tl: '╭', tr: '', bl: '╰', br: '╯', h: '─', v: '│',
3207
+ pad: (s, w) => {
3208
+ // Pad string to width, accounting for chalk ANSI codes
3209
+ const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
3210
+ const diff = w - visible.length;
3211
+ return diff > 0 ? s + ' '.repeat(diff) : s;
3212
+ },
3213
+ };
3214
+ const relevanceBar = (pct, len = 20) => {
3215
+ const filled = Math.round((pct / 100) * len);
3216
+ const empty = len - filled;
3217
+ const color = pct >= 70 ? chalk.hex('#4ADE80') : pct >= 40 ? chalk.hex('#FBBF24') : chalk.hex('#F87171');
3218
+ return color('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
3219
+ };
3220
+ const categoryColor = (cat) => {
3221
+ const colors = {
3222
+ pattern: '#A78BFA', preference: '#67E8F9', skill: '#4ADE80',
3223
+ project: '#FB923C', relationship: '#F472B6',
3224
+ };
3225
+ return chalk.hex(colors[cat] || '#A78BFA');
3226
+ };
3227
+ const categoryChip = (cat) => {
3228
+ const c = categoryColor(cat);
3229
+ return c(` ${cat.toUpperCase()} `);
3230
+ };
3231
+ // ── Header box ──
3232
+ console.log();
3233
+ console.log(chalk.hex('#A78BFA')(` ${box.tl}${box.h.repeat(W)}${box.tr}`));
3234
+ console.log(chalk.hex('#A78BFA')(` ${box.v}`) + box.pad(` ${chalk.hex('#A78BFA').bold('◆ DREAM ENGINE')} ${chalk.dim('memory consolidation')}`, W) + chalk.hex('#A78BFA')(box.v));
3235
+ console.log(chalk.hex('#A78BFA')(` ${box.bl}${box.h.repeat(W)}${box.br}`));
3236
+ console.log();
3237
+ // ── Stats row ──
3238
+ const lastDreamDisplay = state.lastDream
3239
+ ? new Date(state.lastDream).toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })
3240
+ : chalk.dim('never');
3241
+ console.log(` ${chalk.bold('Cycles')} ${chalk.hex('#A78BFA')(String(state.cycles))} ${chalk.bold('Last')} ${lastDreamDisplay} ${chalk.bold('Active')} ${chalk.hex('#4ADE80')(String(state.activeInsights))}`);
3242
+ console.log(` ${chalk.bold('Total')} ${chalk.dim(String(state.totalInsights))} ${chalk.bold('Archived')} ${chalk.dim(`${state.totalArchived} (${archiveCount} files)`)}`);
3243
+ console.log();
3197
3244
  if (insights.length > 0) {
3245
+ // ── Average relevance bar ──
3198
3246
  const avgRel = Math.round(insights.reduce((s, i) => s + i.relevance, 0) / insights.length * 100);
3199
- console.log(`Avg relevance: ${avgRel}%`);
3200
- console.log(chalk.bold('\nTop insights:'));
3247
+ console.log(` ${chalk.bold('Avg Relevance')} ${relevanceBar(avgRel, 24)} ${chalk.bold(`${avgRel}%`)}`);
3248
+ console.log();
3249
+ // ── Category breakdown ──
3250
+ const catCounts = {};
3251
+ for (const i of insights)
3252
+ catCounts[i.category] = (catCounts[i.category] || 0) + 1;
3253
+ const chips = Object.entries(catCounts)
3254
+ .sort((a, b) => b[1] - a[1])
3255
+ .map(([cat, count]) => `${categoryChip(cat)} ${chalk.dim(`${count}`)}`)
3256
+ .join(' ');
3257
+ console.log(` ${chalk.bold('Categories')} ${chips}`);
3258
+ console.log();
3259
+ // ── Divider ──
3260
+ console.log(chalk.dim(` ${'─'.repeat(W)}`));
3261
+ console.log();
3262
+ // ── Top insights ──
3263
+ console.log(` ${chalk.bold('Top Insights')}`);
3264
+ console.log();
3201
3265
  for (const i of insights.slice(0, 8)) {
3202
3266
  const pct = Math.round(i.relevance * 100);
3203
- console.log(` ${chalk.green(`${pct}%`)} [${chalk.cyan(i.category)}] ${i.content}`);
3267
+ const bar = relevanceBar(pct, 12);
3268
+ const tag = categoryChip(i.category);
3269
+ console.log(` ${bar} ${chalk.bold(`${pct}%`)} ${tag}`);
3270
+ console.log(` ${chalk.white(i.content)}`);
3271
+ if (i.keywords.length > 0) {
3272
+ console.log(` ${chalk.dim(i.keywords.map(k => `#${k}`).join(' '))} ${chalk.dim('·')} ${chalk.dim(`${i.sessions} sessions`)}`);
3273
+ }
3274
+ console.log();
3204
3275
  }
3205
3276
  }
3206
3277
  else {
3207
- console.log(chalk.dim('\nNo insights yet. Run: kbot dream run'));
3278
+ console.log(chalk.dim(` ${'─'.repeat(W)}`));
3279
+ console.log();
3280
+ console.log(` ${chalk.dim('No insights yet.')}`);
3281
+ console.log(` ${chalk.dim('Run:')} ${chalk.hex('#A78BFA')('kbot dream run')} ${chalk.dim('to start consolidating memories')}`);
3282
+ console.log();
3208
3283
  }
3209
3284
  });
3210
3285
  dreamCmd
@@ -3213,24 +3288,137 @@ async function main() {
3213
3288
  .action(async (query) => {
3214
3289
  const { searchDreams } = await import('./dream.js');
3215
3290
  const results = searchDreams(query);
3291
+ // Helpers
3292
+ const relevanceBar = (pct, len = 12) => {
3293
+ const filled = Math.round((pct / 100) * len);
3294
+ const empty = len - filled;
3295
+ const color = pct >= 70 ? chalk.hex('#4ADE80') : pct >= 40 ? chalk.hex('#FBBF24') : chalk.hex('#F87171');
3296
+ return color('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
3297
+ };
3298
+ const categoryColor = (cat) => {
3299
+ const colors = {
3300
+ pattern: '#A78BFA', preference: '#67E8F9', skill: '#4ADE80',
3301
+ project: '#FB923C', relationship: '#F472B6',
3302
+ };
3303
+ return chalk.hex(colors[cat] || '#A78BFA');
3304
+ };
3305
+ const highlightQuery = (text, q) => {
3306
+ const terms = q.toLowerCase().split(/\s+/);
3307
+ let result = text;
3308
+ for (const term of terms) {
3309
+ const regex = new RegExp(`(${term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
3310
+ result = result.replace(regex, chalk.hex('#FBBF24').bold.underline('$1'));
3311
+ }
3312
+ return result;
3313
+ };
3216
3314
  if (results.length === 0) {
3217
- printInfo(`No insights match "${query}"`);
3315
+ console.log();
3316
+ console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Search')} ${chalk.dim(`"${query}"`)}`);
3317
+ console.log();
3318
+ console.log(` ${chalk.dim('No insights match this query.')}`);
3319
+ console.log(` ${chalk.dim('Try broader keywords or run')} ${chalk.hex('#A78BFA')('kbot dream run')} ${chalk.dim('first.')}`);
3320
+ console.log();
3218
3321
  return;
3219
3322
  }
3220
- console.log(chalk.bold(`\n${results.length} insights matching "${query}":\n`));
3323
+ console.log();
3324
+ console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Search')} ${chalk.dim(`"${query}"`)} ${chalk.hex('#4ADE80')(`${results.length} found`)}`);
3325
+ console.log(chalk.dim(` ${'─'.repeat(52)}`));
3326
+ console.log();
3221
3327
  for (const i of results.slice(0, 15)) {
3222
3328
  const pct = Math.round(i.relevance * 100);
3223
- console.log(` ${chalk.green(`${pct}%`)} [${chalk.cyan(i.category)}] ${i.content}`);
3224
- console.log(chalk.dim(` ${i.keywords.join(', ')} | ${i.sessions} sessions | ${i.created.split('T')[0]}`));
3329
+ const bar = relevanceBar(pct);
3330
+ const tag = categoryColor(i.category)(` ${i.category.toUpperCase()} `);
3331
+ console.log(` ${bar} ${chalk.bold(`${pct}%`)} ${tag}`);
3332
+ console.log(` ${highlightQuery(i.content, query)}`);
3333
+ const keywordsHighlighted = i.keywords.map(k => highlightQuery(`#${k}`, query)).join(' ');
3334
+ console.log(` ${keywordsHighlighted} ${chalk.dim('·')} ${chalk.dim(`${i.sessions} sessions`)} ${chalk.dim('·')} ${chalk.dim(i.created.split('T')[0])}`);
3335
+ console.log();
3225
3336
  }
3226
3337
  });
3227
3338
  dreamCmd
3228
3339
  .command('journal')
3229
3340
  .description('Print the full dream journal')
3230
3341
  .action(async () => {
3231
- const { getDreamPrompt } = await import('./dream.js');
3232
- const journal = getDreamPrompt(50);
3233
- console.log(journal || chalk.dim('Dream journal is empty. Run: kbot dream run'));
3342
+ const { getDreamStatus } = await import('./dream.js');
3343
+ const { state, insights } = getDreamStatus();
3344
+ // Helpers
3345
+ const W = 56;
3346
+ const box = {
3347
+ tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│',
3348
+ pad: (s, w) => {
3349
+ const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
3350
+ const diff = w - visible.length;
3351
+ return diff > 0 ? s + ' '.repeat(diff) : s;
3352
+ },
3353
+ };
3354
+ const relevanceBar = (pct, len = 16) => {
3355
+ const filled = Math.round((pct / 100) * len);
3356
+ const empty = len - filled;
3357
+ const color = pct >= 70 ? chalk.hex('#4ADE80') : pct >= 40 ? chalk.hex('#FBBF24') : chalk.hex('#F87171');
3358
+ return color('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
3359
+ };
3360
+ const categoryColors = {
3361
+ pattern: '#A78BFA', preference: '#67E8F9', skill: '#4ADE80',
3362
+ project: '#FB923C', relationship: '#F472B6',
3363
+ };
3364
+ const categoryIcon = {
3365
+ pattern: '◇', preference: '♡', skill: '⚡', project: '▸', relationship: '◈',
3366
+ };
3367
+ const categoryColor = (cat) => chalk.hex(categoryColors[cat] || '#A78BFA');
3368
+ if (insights.length === 0) {
3369
+ console.log();
3370
+ console.log(` ${chalk.hex('#A78BFA')('◆')} ${chalk.bold('Dream Journal')}`);
3371
+ console.log();
3372
+ console.log(` ${chalk.dim('The journal is empty.')}`);
3373
+ console.log(` ${chalk.dim('Run')} ${chalk.hex('#A78BFA')('kbot dream run')} ${chalk.dim('after a session to consolidate memories.')}`);
3374
+ console.log();
3375
+ return;
3376
+ }
3377
+ // ── Header ──
3378
+ console.log();
3379
+ console.log(chalk.hex('#A78BFA')(` ${box.tl}${box.h.repeat(W)}${box.tr}`));
3380
+ const headerContent = ` ${chalk.hex('#A78BFA').bold('◆ DREAM JOURNAL')} ${chalk.dim(`${insights.length} insights · cycle ${state.cycles}`)}`;
3381
+ console.log(chalk.hex('#A78BFA')(` ${box.v}`) + box.pad(headerContent, W) + chalk.hex('#A78BFA')(box.v));
3382
+ console.log(chalk.hex('#A78BFA')(` ${box.bl}${box.h.repeat(W)}${box.br}`));
3383
+ console.log();
3384
+ // ── Group by category ──
3385
+ const grouped = {};
3386
+ for (const i of insights) {
3387
+ if (!grouped[i.category])
3388
+ grouped[i.category] = [];
3389
+ grouped[i.category].push(i);
3390
+ }
3391
+ // Sort categories by total insight count descending
3392
+ const catOrder = Object.entries(grouped).sort((a, b) => b[1].length - a[1].length);
3393
+ for (const [cat, catInsights] of catOrder) {
3394
+ const icon = categoryIcon[cat] || '●';
3395
+ const cc = categoryColor(cat);
3396
+ // ── Category section header ──
3397
+ console.log(` ${cc(`${icon} ${cat.toUpperCase()}`)} ${chalk.dim(`(${catInsights.length})`)}`);
3398
+ console.log(` ${cc('─'.repeat(W))}`);
3399
+ console.log();
3400
+ for (const i of catInsights) {
3401
+ const pct = Math.round(i.relevance * 100);
3402
+ const bar = relevanceBar(pct);
3403
+ const date = i.created.split('T')[0];
3404
+ const reinforced = i.lastReinforced !== i.created
3405
+ ? chalk.dim(` · reinforced ${i.lastReinforced.split('T')[0]}`)
3406
+ : '';
3407
+ // Card: relevance bar + content + metadata
3408
+ console.log(` ${bar} ${chalk.bold(`${pct}%`)} ${chalk.dim(`${i.sessions} sessions`)}${reinforced}`);
3409
+ console.log(` ${chalk.white(i.content)}`);
3410
+ if (i.keywords.length > 0) {
3411
+ console.log(` ${chalk.dim(i.keywords.map(k => `#${k}`).join(' '))}`);
3412
+ }
3413
+ console.log(` ${chalk.dim(`${date} · ${i.source} · ${i.id}`)}`);
3414
+ console.log();
3415
+ }
3416
+ }
3417
+ // ── Footer ──
3418
+ console.log(chalk.dim(` ${'─'.repeat(W)}`));
3419
+ const avgRel = Math.round(insights.reduce((s, i) => s + i.relevance, 0) / insights.length * 100);
3420
+ console.log(` ${chalk.dim(`${insights.length} active insights · avg relevance ${avgRel}% · ${state.totalArchived} archived`)}`);
3421
+ console.log();
3234
3422
  });
3235
3423
  program.parse(process.argv);
3236
3424
  const opts = program.opts();
@@ -3973,6 +4161,17 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
3973
4161
  printInfo(`${p.name}`);
3974
4162
  }
3975
4163
  const sessionCount = incrementSessions();
4164
+ // Buddy greeting — Tamagotchi companion appears at startup
4165
+ {
4166
+ const buddy = getBuddy();
4167
+ const isFirstRun = sessionCount <= 1 && !existsSync(join(homedir(), '.kbot', 'config.json'));
4168
+ const greeting = isFirstRun
4169
+ ? `Hey! I'm ${buddy.name} the ${buddy.species}. Let's set up your API key!`
4170
+ : getBuddyGreeting();
4171
+ console.log();
4172
+ console.log(formatBuddyStatus(greeting));
4173
+ console.log();
4174
+ }
3976
4175
  // Seed knowledge on first run — give new users a head start
3977
4176
  if (sessionCount <= 2) {
3978
4177
  try {
@@ -0,0 +1,36 @@
1
+ import type { DreamInsight, DreamCategory } from './dream.js';
2
+ export interface AnonymizedInsight {
3
+ /** Category preserved as-is */
4
+ category: DreamCategory;
5
+ /** Keywords with PII stripped */
6
+ keywords: string[];
7
+ /** Generalized version of the content (no personal details) */
8
+ generalizedContent: string;
9
+ /** How many contributors have shared similar insights */
10
+ contributorCount: number;
11
+ /** First time this insight appeared in the collective */
12
+ firstSeen: string;
13
+ /** Most recent contribution timestamp */
14
+ lastSeen: string;
15
+ }
16
+ /**
17
+ * Anonymize a single dream insight for collective sharing.
18
+ * Strips personal info, keeps only category, keywords, and generalized content.
19
+ */
20
+ export declare function anonymizeDreamInsight(insight: DreamInsight): AnonymizedInsight;
21
+ /**
22
+ * Prepare a batch of dream insights for collective sharing.
23
+ * 1. Filter to high-relevance insights (> 0.7)
24
+ * 2. Anonymize each
25
+ * 3. Deduplicate by content similarity
26
+ */
27
+ export declare function prepareCollectiveDreams(insights: DreamInsight[]): AnonymizedInsight[];
28
+ /**
29
+ * Merge collective wisdom into the local dream journal.
30
+ *
31
+ * Collective insights are injected with a lower base relevance (0.5) so they
32
+ * don't drown out the user's own insights but still surface when relevant.
33
+ * Deduplicates against existing local insights by content similarity.
34
+ */
35
+ export declare function mergeCollectiveDreams(local: DreamInsight[], collective: AnonymizedInsight[]): DreamInsight[];
36
+ //# sourceMappingURL=collective-dreams.d.ts.map